import logging
from dataclasses import dataclass
from pathlib import Path
from typing import List, Dict, Tuple, Optional, Union


@dataclass
class CodeFile:
    path: Path
    content: List[str]
    line_count: int

class CProjectLoader:
    def __init__(self, project_path: Union[Path, str]):
        """
        初始化C项目代码加载器

        Args:
            project_path: C项目的根目录路径
        """

        # 加载项目中的所有.c、.cpp和.h文件
        self.extensions = {'.c', '.cpp', '.h'}

        self.logger = logging.getLogger("loader")

        self.project_path = Path(project_path).resolve()
        if not self.project_path.exists():
            raise FileNotFoundError(f"项目路径不存在: {project_path}")

        self.files: Dict[str, CodeFile] = {}
        self._load_project()

    def _load_project(self) -> None:
        """加载项目中的所有.c、.cpp和.h文件"""
        for file_path in self.project_path.rglob('*'):
            if file_path.suffix.lower() in self.extensions:
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.readlines()
                        file_path = file_path.absolute()
                        self.files[str(file_path)] = CodeFile(
                            path=file_path,
                            content=content,
                            line_count=len(content)
                        )
                except Exception as e:
                    self.logger.error(f"无法读取文件 {file_path}: {str(e)}")

    def get_file_content(self, file_path: Union[Path, str]) -> Optional[List[str]]:
        """
        获取指定文件的完整内容

        Args:
            file_path: 文件路径（相对于项目根目录或绝对路径）

        Returns:
            文件内容的行列表，如果文件不存在则返回None
        """
        path = Path(file_path)
        if not path.is_absolute():
            path = self.project_path / path
        path = str(path.resolve())

        code_file = self.files.get(path)
        return code_file.content if code_file else None

    def get_full_file_content(self, file_path: Union[Path, str]) -> str | None:
        """
        获取指定文件的拼接的完整内容

        Args:
            file_path: 文件路径（相对于项目根目录或绝对路径）

        Returns:
            文件内容，如果文件不存在则返回None
        """
        content = self.get_file_content(file_path)
        return ''.join(content) if content else None

    def get_file_lines(self, file_path: Union[Path, str], start_line: int, end_line: Optional[int] = None) -> Optional[List[str]]:
        """
        获取指定文件的特定行范围内容

        Args:
            file_path: 文件路径
            start_line: 起始行号（从1开始）
            end_line: 结束行号（包含），如果不指定则只返回start_line行

        Returns:
            指定范围的代码行列表，如果文件不存在或行号无效则返回None
        """
        content = self.get_file_content(file_path)
        if not content:
            return None

        if start_line < 1:
            return None

        if end_line is None:
            end_line = start_line

        if end_line > len(content):
            return None

        return content[start_line-1:end_line]

    def get_all_files(self) -> List[Path]:
        """获取项目中所有已加载的源代码文件路径"""
        return list(map(Path, self.files.keys()))

    def search_code(self, keyword: str) -> List[Tuple[Path, int, str]]:
        """
        在所有文件中搜索包含指定关键词的代码行

        Args:
            keyword: 要搜索的关键词

        Returns:
            包含匹配行信息的列表，每项为(文件路径, 行号, 行内容)的元组
        """
        results = []
        for file_path, code_file in self.files.items():
            for line_num, line in enumerate(code_file.content, 1):
                if keyword in line:
                    results.append((Path(file_path), line_num, line))
        return results

    def find_block_end(self, file_path: Union[Path, str], start_line: int) -> Optional[int]:
        """
        根据开始行查找代码块的结束行（通过匹配大括号）

        Args:
            file_path: 文件路径
            start_line: 代码块开始行号（从1开始）

        Returns:
            代码块结束行号，如果无法找到匹配的结束大括号则返回None

        Note:
            - 会处理嵌套的大括号
            - 忽略字符串和注释中的大括号
            - 如果start_line中没有匹配到左大括号，则返回None
        """
        content = self.get_file_content(file_path)
        if not content or start_line < 1 or start_line > len(content):
            return None
        first_line = content[start_line - 1].strip()
        if first_line.find('{') == -1 and first_line[-1] == ';':
            return None

        brace_count = 0
        in_string = False
        in_line_comment = False
        in_block_comment = False

        # 从开始行扫描到文件末尾
        for line_num in range(start_line - 1, len(content)):
            line = content[line_num]
            i = 0
            while i < len(line):
                # 处理行注释
                if not in_string and not in_block_comment and line[i:i+2] == '//':
                    break  # 忽略行注释后的内容

                # 处理块注释开始
                if not in_string and not in_line_comment and line[i:i+2] == '/*':
                    in_block_comment = True
                    i += 2
                    continue

                # 处理块注释结束
                if in_block_comment and line[i:i+2] == '*/':
                    in_block_comment = False
                    i += 2
                    continue

                # 在注释中忽略所有字符
                if in_block_comment:
                    i += 1
                    continue

                # 处理字符串
                if not in_line_comment and not in_block_comment:
                    if line[i] == '"' and (i == 0 or line[i-1] != '\\'):
                        in_string = not in_string
                    elif not in_string:
                        if line[i] == '{':
                            brace_count += 1
                        elif line[i] == '}':
                            brace_count -= 1
                            if brace_count == 0:
                                return line_num + 1  # 找到匹配的结束大括号

                i += 1

            # 重置行级标志
            in_line_comment = False

        # 如果没有找到匹配的结束大括号
        return None

# 使用示例
if __name__ == "__main__":
    # 创建加载器实例
    loader = CProjectLoader("../example_project/test_project")

    # 获取所有文件
    all_files = loader.get_all_files()
    print(f"项目中共有 {len(all_files)} 个源代码文件")

    start = 24
    end = loader.find_block_end("include/data_types.h", start)

    # 读取特定文件的特定行
    file_content = loader.get_file_lines("include/data_types.h", start, end)
    if file_content:
        print(f"第{start}-{end}行的内容：")
        for line_content in file_content:
            print(line_content, end='')
    else:
        print("找不到")
